home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C++ / Applications / Nuntius 1.2 / src / Nuntius / UPostArticleCmds.cp < prev    next >
Encoding:
Text File  |  1994-03-20  |  22.0 KB  |  892 lines  |  [TEXT/MPS ]

  1. // Copyright © 1992 Peter Speck, speck@dat.ruc.dk. All rights reserved.
  2. // UPostArticleCmds.cp
  3.  
  4. #include "UPostArticleCmds.h"
  5. #include "UArticle.h"
  6. #include "UArticleCache.h"
  7. #include "UGroupDoc.h"
  8. #include "UDiscList.h"
  9. #include "UPrefsDatabase.h"
  10. #include "FileTools.h"
  11. #include "Tools.h"
  12. #include "UNntp.h"
  13. #include "UMacTCP.h"
  14. #include "UProgress.h"
  15. #include "ProcessTools.h"
  16. #include "NetAsciiTools.h"
  17. #include "ISO2022Conversion.h"
  18. #include "UBufferedFileStream.h"
  19. #include "UThread.h"
  20. #include "UFatalError.h"
  21.  
  22. #include <ErrorGlobals.h>
  23. #include <RsrcGlobals.h>
  24.  
  25. #include <Packages.h>
  26. #include <Errors.h>
  27. #include <UDialog.h>
  28. #include <Folders.h>
  29. #include <Resources.h>
  30. #include <ToolUtils.h>
  31. #include <OSUtils.h>
  32. #include <Script.h>
  33.  
  34. #pragma segment MyArticle
  35.  
  36. #define qDebugGarbageCollector qDebug & 0
  37. //==================================================================================================
  38. PPostArticleInfo::PPostArticleInfo()
  39. {
  40.     fSubject = "";
  41.     fNewsGroups = "";
  42.     fReferences = "";
  43.     fDistribution = "";
  44.     fFile = nil;
  45.     fDefaultTextH = nil;
  46.     fRefCount = 0;
  47.     fEditHeaders = false;
  48.     fEditSignature = false;
  49.     fAddSignatureChoice = false;
  50.     fTrashDiskCopyAfterPost = false;
  51.     fNntp = nil;
  52. }
  53.  
  54. void PPostArticleInfo::IPostArticleInfo()
  55. {
  56.     fEditHeaders = gPrefs->GetBooleanPrefs('EdHe');
  57.     fEditSignature = gPrefs->GetBooleanPrefs('EdSi');
  58.     fAddSignatureChoice = gPrefs->GetBooleanPrefs('DUSi');
  59.     fDefaultTextH = NewPermHandle(0);
  60. }
  61.  
  62. PPostArticleInfo::~PPostArticleInfo()
  63. {
  64.     gNntpCache->ReturnNntp(fNntp); fNntp = nil;
  65.     FreeIfObject(fFile); fFile = nil;
  66.     fDefaultTextH = DisposeIfHandle(fDefaultTextH);
  67. }
  68.  
  69. void PPostArticleInfo::Cancel()
  70. {
  71.     fCreateArticleW->CloseByUser();
  72.     fCreateArticleW = nil;
  73.     fPostArticleW->CloseByUser();
  74.     fPostArticleW = nil;
  75.     delete this;
  76. }
  77.  
  78. void PPostArticleInfo::CreateWindows()
  79. {
  80.     fCreateArticleW = gViewServer->NewTemplateWindow(kEditArticleToPostView, nil);
  81. #if qDebug
  82.     if (!IsObject(fCreateArticleW))
  83.         ProgramBreak("fCreateArticleW is nil");
  84. #endif
  85.     fCreateArticleView = (TEditArticleToPostDialogView*) (fCreateArticleW->FindSubView('DLOG'));
  86. #if qDebug
  87.     if (!IsObject(fCreateArticleView))
  88.         ProgramBreak("fCreateArticleView not found");
  89. #endif
  90.     fCreateArticleView->SetInfo(this);
  91.     fCreateArticleW->Open();
  92. //
  93.     fPostArticleW = gViewServer->NewTemplateWindow(kPostArticleView, nil);
  94. #if qDebug
  95.     if (!IsObject(fPostArticleW))
  96.         ProgramBreak("fPostArticleW not created");
  97. #endif
  98.     fPostArticleView = (TPostArticleDialogView*) (fPostArticleW->FindSubView('DLOG'));
  99. #if qDebug
  100.     if (!IsObject(fPostArticleView))
  101.         ProgramBreak("fPostArticleView not found");
  102. #endif
  103.     fPostArticleView->SetInfo(this);
  104. }
  105.  
  106. void PPostArticleInfo::ShowPostArticleWindow()
  107. {
  108.     fCreateArticleW->Close();
  109.     fPostArticleView->SetInfo(this); // setup Subject string
  110.     fPostArticleW->Select();
  111.     fPostArticleW->Open();
  112. }
  113. //--------------------------------------------------------------------------
  114. void PPostArticleInfo::DoPostArticle()
  115. {
  116.     fCreateArticleW->Close();
  117.     fPostArticleW->Close();
  118.     TPostArticleCommand *command = new TPostArticleCommand();
  119.     command->IPostArticleCommand(this);
  120.     gApplWideThreads->ExecuteCommand(command, "TPostArticleCommand");
  121. }
  122.  
  123. void PPostArticleInfo::CreateNntp()
  124. {
  125.     if (fNntp)
  126.         return;
  127.     fNntp = gNntpCache->GetNntp();
  128.     if (!fNntp->IsPostingAllowed())
  129.         FailOSErr(errNotAllowedToPost);
  130. }
  131.  
  132. void PPostArticleInfo::DiscardNntp()
  133. {
  134.     gNntpCache->DiscardNntp(fNntp); fNntp = nil;
  135. }
  136.  
  137. //--------------------------------------------------------------------------
  138. void PPostArticleInfo::MakeFile()
  139. {
  140.     TStream *aStream = nil;
  141.     VOLATILE(aStream);
  142.     TFile *file = nil;
  143.     VOLATILE(file);
  144.     FailInfo fi;
  145.     if (fi.Try())
  146.     {
  147.         OSType signature;
  148.         if (gPrefs->PrefExists('EDsi'))
  149.             signature = gPrefs->GetSignaturePrefs('EDsi');
  150.         else
  151.             signature = 'ttxt'; // TeachText
  152.         file = NewFile('TEXT', signature,
  153.             kUsesDataFork, noResourceFork, !kDataOpen, !kRsrcOpen);
  154.         file->SetPermissions(fsRdWrPerm, fsRdWrPerm);
  155.         FSSpec spec;
  156.         gPrefs->GetSilentDirAliasPrefs('FEdi', spec);
  157.         CStr255 name(fSubject);
  158.         CheckFilenameSanity(name);
  159.         CopyCString2String(name, spec.name);
  160.         file->Specify(spec);
  161.         MakeFilenameUnique(file);
  162.         FailOSErr(file->CreateFile());
  163.         FailOSErr(file->OpenFile());
  164.         aStream = NewBufferedFileStream(file, 0, 16 * 1024);
  165.         
  166.         if (fEditHeaders)
  167.             MakeHeader(aStream, kPostArticleHeaderStrings);
  168.         
  169.         HLock(fDefaultTextH);
  170.         aStream->WriteBytes(*fDefaultTextH, GetHandleSize(fDefaultTextH));
  171.         HUnlock(fDefaultTextH);
  172.         
  173.         if (fEditSignature && fAddSignatureChoice)
  174.             MakeSignature(aStream);
  175.         
  176.         aStream->Free(); aStream = nil;
  177.         FailOSErr(file->CloseFile());
  178.         fFile = file; file = nil;
  179.         fFile->Modified();
  180.         fi.Success();
  181.     }
  182.     else // fail
  183.     {
  184.         FreeIfObject(aStream); aStream = nil;
  185.         FreeIfObject(file); file = nil;
  186.         fi.ReSignal();
  187.     }
  188. }
  189.  
  190. void PPostArticleInfo::LaunchEditor()
  191. {
  192.     FailNIL(fFile);
  193.     AliasHandle aliasH;
  194.     fFile->GetAlias(aliasH);
  195.     VOLATILE(aliasH);
  196.     FailInfo fi;
  197.     if (fi.Try())
  198.     {
  199.         FSSpec spec;
  200.         gPrefs->GetAliasPrefs('EDid', spec);
  201.         OpenApplicationDocument(spec, aliasH, true);
  202.         DisposeIfHandle(Handle(aliasH));
  203.         fi.Success();
  204.     }
  205.     else // fail
  206.     {
  207.         DisposeIfHandle(Handle(aliasH));
  208.         if (fi.error)
  209.             gApplication->ShowError(fi.error, messageLaunchEditorFailed);
  210.     }
  211. }
  212.  
  213. //==========================================================================
  214.  
  215. void WriteString(TStream *aStream, const CStr255 &text)
  216. {
  217.     CStr255 s(text); //?? until CStr255::operator[] const is inlined
  218.     aStream->WriteBytes(&s[1], text.Length());
  219. }
  220.  
  221. void Num2TwoDigitString(long l, CStr255 &s)
  222. {
  223.     s.Length() = 2;
  224.     s[1] = char('0' + l / 10);
  225.     s[2] = char('0' + (l % 10));
  226. }
  227.  
  228. void PPostArticleInfo::MakeHeader(TStream *aStream, short templateID)
  229. {
  230. #if qDebug
  231.     if (!IsObject(aStream))
  232.         ProgramBreak("aStream is not object");
  233. #endif
  234.     CStr255 s, realname, username, email, machinename, dotname;
  235.     CStr255 nuntiusversion, organization, messageID;
  236.  
  237.     gPrefs->GetStringPrefs('@adr', email);
  238.     username = email;
  239.     short pos = username.Pos("@");
  240.     if (pos)
  241.         username.Delete(pos, username.Length() - pos + 1);
  242.  
  243.     // bit trouble, as we have to be able to post from an un(dot)named mac
  244.     FailInfo fi;
  245.     if (fi.Try())
  246.     {
  247.         GetMyDotName(dotname);
  248.         fi.Success();
  249.     }
  250.     else // fail
  251.     {
  252.         if (fi.error == errMyMacHasNoDotName || fi.error == noNameServer || fi.error == authNameErr || fi.error == noAnsErr || fi.error == dnrErr)
  253.             dotname = "";
  254.         else
  255.             fi.ReSignal();
  256.     }
  257.     machinename = dotname;
  258.     pos = machinename.Pos(".");
  259.     if (pos)
  260.         machinename.Delete(pos, machinename.Length() - pos + 1);
  261.  
  262.     UseResFile(gApplicationRefNum); // just to be sure...
  263.     Handle h = GetResource('vers', 1);
  264.     FailNILResource(h);
  265.     HNoPurge(h);
  266.     nuntiusversion = VersRecPtr(*h)->shortVersion;
  267.     gPrefs->GetStringPrefs('Orga', organization);
  268.     gPrefs->GetStringPrefs('Name', realname);
  269.     
  270.     static unsigned long noHeader = 0;
  271.     unsigned long tc = TickCount();
  272.     unsigned long dati;
  273.     GetDateTime(dati);
  274.     long l2 = (MyIP() & 0xFFFF);
  275.     l2 |= (++noHeader & 0xFF) << 16; 
  276.     l2 |= (tc & 0xFF) << 24;
  277.     messageID = '<';
  278.     Long2Hex(dati, s);
  279.     messageID += s;
  280.     Long2Hex(l2, s);
  281.     messageID += s;
  282.     messageID += '@';
  283.     messageID += dotname;
  284.     messageID += '>';
  285.  
  286.     //    Wdy, DD Mon YY HH:MM:SS TIMEZONE
  287.     DateTimeRec dt;
  288.     Secs2Date(dati, dt);
  289.     CStr255 year, month, day, hour, minute, second, dayofweek, gmthour;
  290.     NumToString(dt.year, year);
  291.     GetIndString(s, kPostArticleMonthNames, dt.month);
  292.     month = s;
  293.     NumToString(dt.day, s);
  294.     day = s;
  295.     Num2TwoDigitString(dt.hour, hour);
  296.     Num2TwoDigitString(dt.minute, minute);
  297.     Num2TwoDigitString(dt.second, second);
  298.     GetIndString(s, kPostArticleWeekDayNames, dt.dayOfWeek);
  299.     dayofweek = s;
  300.  
  301.     MachineLocation loc;
  302.     ReadLocation(loc);
  303.     long gmt = dt.hour + 24 - short(loc.gmtFlags.gmtDelta & 0xFFFF) / 3600;
  304.     Num2TwoDigitString(gmt % 24, gmthour);
  305.  
  306.     h = GetResource('STR#', templateID);
  307.     FailNILResource(h);
  308.     short numHeaders = *( (short*) *h);
  309.     for (short index = 1; index <= numHeaders; index++)
  310.     {
  311.         GetIndString(s, templateID, index);
  312.         SubstituteStringItems(s, "«realname»", realname);
  313.         SubstituteStringItems(s, "«username»", username);
  314.         SubstituteStringItems(s, "«email»", email);
  315.         if (SubstituteStringItems(s, "«machinename»", machinename) && !machinename.Length())
  316.             continue;
  317.         if (SubstituteStringItems(s, "«dotname»", dotname) && !dotname.Length())
  318.             continue;
  319.         SubstituteStringItems(s, "«newsgroups»", fNewsGroups);
  320.         SubstituteStringItems(s, "«subject»", fSubject);
  321.         SubstituteStringItems(s, "«nuntiusversion»", nuntiusversion);
  322.         if (SubstituteStringItems(s, "«references»", fReferences) && !fReferences.Length())
  323.             continue;
  324.         if (SubstituteStringItems(s, "«distribution»", fDistribution) && !fDistribution.Length())
  325.             continue;
  326.         SubstituteStringItems(s, "«organization»", organization);
  327.         if (SubstituteStringItems(s, "«message-id»", messageID) && !dotname.Length())
  328.             continue;
  329. // date
  330.         SubstituteStringItems(s, "«year»", year);
  331.         SubstituteStringItems(s, "«month»", month);
  332.         SubstituteStringItems(s, "«day»", day);
  333.         SubstituteStringItems(s, "«minute»", minute);
  334.         SubstituteStringItems(s, "«second»", second);
  335.         SubstituteStringItems(s, "«dayofweek»", dayofweek);
  336.         SubstituteStringItems(s, "«hour»", hour);
  337.         SubstituteStringItems(s, "«gmt-hour»", gmthour);
  338.  
  339. //
  340.         if (s.Pos("«") || s.Pos("»"))
  341.         {
  342. #if qDebug
  343.             fprintf(stderr, "Unknown variable in header: %s\n", (char*)s);
  344. #endif
  345.             continue; // unknown variable, do not emit header
  346.         }
  347.         if (!s.Length() || s[s.Length()] != '\n')
  348.             s += "\n";
  349.         WriteString(aStream, s);
  350.     }
  351. }
  352.  
  353. void PPostArticleInfo::MakeSignature(TStream *aStream)
  354. {
  355. #if qDebug
  356.     if (!IsObject(aStream))
  357.         ProgramBreak("aStream is not object");
  358. #endif
  359.     if (!gPrefs->PrefExists('Sigu'))
  360.         return; // no signature file
  361.     TFile *sigFile = nil;
  362.     VOLATILE(sigFile);
  363.     Handle dataH = nil;
  364.     VOLATILE(dataH);
  365.     FailInfo fi;
  366.     if (fi.Try())
  367.     {
  368.         sigFile = NewFile('TEXT', 'ttxt',
  369.             kUsesDataFork, noResourceFork, !kDataOpen, !kRsrcOpen);
  370.         sigFile->SetPermissions(fsRdWrPerm, fsRdWrPerm);
  371.         FSSpec spec;
  372.         gPrefs->GetAliasPrefs('Sigu', spec);
  373.         sigFile->Specify(spec);        
  374.         FailOSErr(sigFile->OpenFile());
  375.         long size;
  376.         FailOSErr(sigFile->GetDataLength(size));
  377.         dataH = NewPermHandle(size);
  378.         HLock(dataH);
  379.         FailOSErr(sigFile->ReadData(*dataH, size));
  380.         aStream->WriteBytes(*dataH, size);
  381.         HUnlock(dataH);
  382.         dataH = DisposeIfHandle(dataH);
  383.         sigFile->CloseFile();
  384.         sigFile->Free(); sigFile = nil;
  385.         fi.Success();
  386.     }
  387.     else // fail
  388.     {
  389.         dataH = DisposeIfHandle(dataH);
  390.         sigFile->Free(); sigFile = nil;
  391.         FailNewMessage(fi.error, fi.message, messageSignatureProblem);
  392.     }
  393. }
  394. //---------------------------------------------------------------------
  395. TCreateNewDiscussionCommand::TCreateNewDiscussionCommand()
  396. {
  397. }
  398.  
  399.  
  400. pascal void TCreateNewDiscussionCommand::Initialize()
  401. {
  402.     inherited::Initialize();
  403. }
  404.  
  405. void TCreateNewDiscussionCommand::ICreateNewDiscussionCommand(TGroupDoc *doc)
  406. {
  407. #if qDebug
  408.     if (!IsObject(doc))
  409.         ProgramBreak("doc is not object");
  410. #endif
  411.     inherited::ICommand(cPostNewDiscussion, nil, false, false, nil);
  412.     PPostArticleInfo *info = new PPostArticleInfo();
  413.     info->IPostArticleInfo();
  414.     fPostArticleInfo = info;
  415.     CStr255 s;
  416.     fDoc = doc;
  417.     fDoc->GetGroupDotName(s);
  418.     fPostArticleInfo->fNewsGroups = s;
  419. }
  420.  
  421. pascal void TCreateNewDiscussionCommand::Free()
  422. {
  423.     inherited::Free();
  424. }
  425.  
  426. pascal void TCreateNewDiscussionCommand::DoIt()
  427. {
  428.     FailInfo fi;
  429.     if (fi.Try())
  430.     {
  431.         if (!gPrefs->GetBooleanPrefs('NaOK'))
  432.             FailOSErr(errMissingYourName);
  433.         if (!gPrefs->PrefExists('EDid'))
  434.             FailOSErr(errNoEditor);
  435.         fPostArticleInfo->CreateNntp();
  436.         PrepareInfo();
  437.         fPostArticleInfo->CreateWindows();
  438.         fi.Success();
  439.     }
  440.     else // fail
  441.     {
  442.         delete fPostArticleInfo; fPostArticleInfo = nil;
  443.         FailNewMessage(fi.error, fi.message, messageCouldNotPost);
  444.     }
  445. }
  446.  
  447. void TCreateNewDiscussionCommand::PrepareInfo()
  448. {
  449. }
  450.  
  451. //.............................................................
  452.  
  453. TCreateFollowupCommand::TCreateFollowupCommand()
  454. {
  455. }
  456.  
  457.  
  458. pascal void TCreateFollowupCommand::Initialize()
  459. {
  460.     inherited::Initialize();
  461.     fDefaultText = nil;
  462.     fArticle = nil;
  463. }
  464.  
  465. void TCreateFollowupCommand::ICreateFollowupCommand(TGroupDoc *doc, ArrayIndex discIndex,
  466.                         Handle defaultText)
  467. {
  468. #if qDebug
  469.     if (!IsObject(doc))
  470.         ProgramBreak("doc is not object");
  471. #endif
  472.     inherited::ICreateNewDiscussionCommand(doc);
  473.     fDefaultText = defaultText;
  474.     fDiscIndex = discIndex;
  475.     fDoc = doc;
  476. }    
  477.  
  478. pascal void TCreateFollowupCommand::Free()
  479. {
  480.     gArticleCache->ReturnArticle(fArticle); fArticle = nil;
  481.     fDefaultText = DisposeIfHandle(fDefaultText);
  482.     inherited::Free();
  483. }
  484.  
  485. void TCreateFollowupCommand::PrepareInfo()
  486. {
  487.     inherited::PrepareInfo();
  488.     PDiscList *discList = fDoc->GetDiscList();
  489.     TLongintList *idList = nil;
  490.     FailInfo fi;
  491.     if (fi.Try())
  492.     {
  493.         idList = discList->GetArticleIDList(fDiscIndex);
  494.         DoPrepareArticleInfo(idList->At(1));
  495.         long noID = idList->GetSize();
  496.         long noQuoted = MinMax(0, noID - 2, 4);
  497.         for (long i = 2; i <= noQuoted; i++)
  498.         {
  499.             CStr255 s;
  500.             long index = noID - noQuoted + i;
  501.             long id = idList->At(index);
  502.             if (id < fDoc->GetFirstArticleID() || id > fDoc->GetLastArticleID())
  503.                 continue;
  504.             if (!GetMsgID(id, s))
  505.                 continue;
  506.             if (fPostArticleInfo->fReferences.Length() + s.Length() > 150)
  507.                 continue;
  508.             fPostArticleInfo->fReferences += ' ';
  509.             fPostArticleInfo->fReferences += s;
  510.         }
  511.         FreeIfObject(idList); idList = nil;
  512. #if qDebug & 0
  513.         CStr255 zzz(fPostArticleInfo->fReferences);
  514.         fprintf(stderr, "References for follow-up: '%s'\n", (char*)zzz);
  515. #endif
  516.  
  517.         DisposeIfHandle(fPostArticleInfo->fDefaultTextH);
  518.         fPostArticleInfo->fDefaultTextH = fDefaultText; fDefaultText = nil;
  519.  
  520.         fi.Success();
  521.     }
  522.     else // fail
  523.     {
  524.         FreeIfObject(idList); idList = nil;
  525.         fi.ReSignal();
  526.     }
  527. }
  528.  
  529. void TCreateFollowupCommand::DoPrepareArticleInfo(long id)
  530. {
  531.     CStr255 groupDotName;
  532.     fDoc->GetGroupDotName(groupDotName);
  533.     fArticle = gArticleCache->GetArticle(groupDotName, id);
  534.     CStr255 s;
  535. // Subject
  536.     fArticle->GetHeader("Subject", s);
  537.     if (s.Copy(1, 4) != "Re: ")
  538.         if (s.Copy(1, 4) != "re: ")
  539.             s.Insert("Re: ", 1);
  540.     fPostArticleInfo->fSubject = s;
  541. // Message-id
  542.     fArticle->GetHeader("Message-id", s);
  543.     fPostArticleInfo->fReferences = s;
  544. // Newsgroups
  545.     if (fArticle->GetHeader("Newsgroups", s))
  546.         fPostArticleInfo->fNewsGroups = s;
  547. // Distribution
  548.     if (fArticle->GetHeader("Distribution", s))
  549.         fPostArticleInfo->fDistribution = s;
  550. //-
  551.     gArticleCache->ReturnArticle(fArticle); fArticle = nil;
  552. }
  553.  
  554. Boolean TCreateFollowupCommand::GetMsgID(long articleID, CStr255 &id)
  555. {
  556.     TArticle *article = nil;
  557.     VOLATILE(article);
  558.     FailInfo fi;
  559.     if (fi.Try())
  560.     {
  561.         CStr255 groupDotName;
  562.         fDoc->GetGroupDotName(groupDotName);
  563.         article = gArticleCache->GetArticle(groupDotName, articleID);
  564.         article->GetHeader("Message-id", id);
  565.         gArticleCache->ReturnArticle(article); article = nil;
  566.         fi.Success();
  567.         return true;
  568.     }
  569.     else // fail
  570.     {
  571.         gArticleCache->ReturnArticle(article); article = nil;
  572.         if (fi.error != errNoSuchArticle)
  573.             fi.ReSignal();
  574.         return false;
  575.     }
  576. }
  577.  
  578. //---------------------------------------------------------------------
  579.  
  580. TPostArticleCommand::TPostArticleCommand()
  581. {
  582. }
  583.  
  584.  
  585. pascal void TPostArticleCommand::Initialize()
  586. {
  587.     inherited::Initialize();
  588.     fText = nil;
  589.     fPostArticleInfo = nil;
  590. }
  591.  
  592. void TPostArticleCommand::IPostArticleCommand(PPostArticleInfo *info)
  593. {
  594.     inherited::ICommand(cPostArticle, nil, false, false, nil);
  595.     fPostArticleInfo = info;
  596. }
  597.  
  598. pascal void TPostArticleCommand::Free()
  599. {
  600.     if (fPostArticleInfo)
  601.     {
  602.         delete fPostArticleInfo;
  603.         fPostArticleInfo = nil;
  604.     }
  605.     DisposeIfHandle(fText); fText = nil;
  606.     inherited::Free();
  607. }
  608.  
  609. pascal void TPostArticleCommand::DoIt()
  610. {
  611.     FailInfo fi;
  612.     if (fi.Try())
  613.     {
  614.         if (fPostArticleInfo->fFile && !fPostArticleInfo->fFile->IsModified())
  615.             FailOSErr(errArticleNotEdited);
  616.  
  617.         gCurProgress->SetStandardProgressType();
  618.         gCurProgress->SetTitle(CStr255(fPostArticleInfo->fSubject));
  619.         gCurProgress->SetText(kPostArticleExaminesArticle);
  620.         gCurProgress->StartProgress(true);
  621.         gCurProgress->SetWorkToDo(kCandyStribes);
  622.  
  623.         ReadArticle();
  624.         PostArticle();
  625.         gCurProgress->WorkDone();
  626.         if (fPostArticleInfo->fTrashDiskCopyAfterPost)
  627.             TrashDiskCopy();
  628.         fi.Success();
  629.     }
  630.     else // fail
  631.     {
  632.         gCurProgress->WorkDone();
  633.         FailNewMessage(fi.error, fi.message, messageCouldNotPostArticle);
  634.     }
  635. }
  636.  
  637. void TPostArticleCommand::ReadArticle()
  638. {
  639.     THandleStream *handleStream = nil;
  640.     VOLATILE(handleStream);
  641.     FailInfo fi;
  642.     if (fi.Try())
  643.     {
  644. // header
  645.         fText = NewPermHandle(0);
  646.         if (!fPostArticleInfo->fEditHeaders)
  647.         {
  648.             THandleStream *hs = new THandleStream();
  649.             hs->IHandleStream(fText, 128);
  650.             handleStream = hs;
  651.             fPostArticleInfo->MakeHeader(handleStream, kPostArticleHeaderStrings);
  652.             handleStream->Free(); handleStream = nil;
  653.         }
  654.  
  655. // body
  656.         TFile *file = fPostArticleInfo->fFile;
  657.         FInfo finderInfo;
  658.         FailOSErr(file->GetFinderInfo(finderInfo));
  659.         if (finderInfo.fdType != 'TEXT')
  660.             FailOSErr(errCannotPostNonTextFiles);
  661.  
  662.         FailOSErr(file->OpenFile());
  663.  
  664.         long headerSize = GetHandleSize(fText);
  665.         long bodySize;
  666.         FailOSErr(file->GetDataLength(bodySize));
  667.         if (gPrefs->GetBooleanPrefs('2022'))
  668.             Export2022Body(bodySize);
  669.         else
  670.         {
  671.             SetPermHandleSize(fText, headerSize + bodySize);
  672.             HLock(fText);
  673.             FailOSErr(file->ReadData(*fText + headerSize, bodySize));
  674.             TranslateViaTable(gMac2NetAscii, *fText + headerSize, bodySize);            
  675.             HUnlock(fText);
  676.         }
  677.         FailOSErr(file->CloseFile());
  678.  
  679. // signature
  680.         if (fPostArticleInfo->fAddSignatureChoice && !fPostArticleInfo->fEditSignature)
  681.         {
  682.             long size = GetHandleSize(fText);
  683.             while (size > headerSize && *( *fText + size - 1) <= 32) // strip off empty lines at end,
  684.                 size--;
  685.             SetPermHandleSize(fText, ++size);
  686.             *( *fText + size - 1) = 13;
  687.             THandleStream *hs = new THandleStream();
  688.             hs->IHandleStream(fText, 256);
  689.             handleStream = hs;
  690.             handleStream->SetPosition(size);
  691.             fPostArticleInfo->MakeSignature(handleStream);
  692.             handleStream->Free(); handleStream = nil;
  693.         }
  694.         long size = GetHandleSize(fText);
  695.         while (size > headerSize && *( *fText + size - 1) <= 32) // strip off empty lines at end,
  696.             size--;
  697.         SetPermHandleSize(fText, ++size);
  698.         *( *fText + size - 1) = 13;
  699.         fi.Success();
  700.     }
  701.     else // fail
  702.     {
  703.         FreeIfObject(handleStream); handleStream = nil;
  704.         if (fPostArticleInfo->fFile)
  705.             fPostArticleInfo->fFile->CloseFile();
  706.         fi.ReSignal();
  707.     }
  708. }
  709.  
  710. void TPostArticleCommand::Export2022Body(long bodySize)
  711. {
  712.     Handle fromH = nil;
  713.     VOLATILE(fromH);
  714.     PHandleCodeConverter *converter = nil;
  715.     VOLATILE(converter);
  716.     FailInfo fi;
  717.     if (fi.Try())
  718.     {
  719.         fromH = NewPermHandle(bodySize);
  720.         HLock(fromH);
  721.         FailOSErr(fPostArticleInfo->fFile->ReadData(*fromH, bodySize));
  722.         HUnlock(fromH);
  723.         PHandleCodeConverter *hcc = new PHandleCodeConverter();
  724.         hcc->IHandleCodeConverter(fromH, 1024);
  725.         converter = hcc;
  726.         converter->ConvertMac2Net();
  727.         long headerSize = GetHandleSize(fText);
  728.         long convertedSize = converter->GetOutputSize();
  729.         SetPermHandleSize(fText, headerSize + convertedSize);
  730.         BytesMove(converter->GetOutputPtr(), *fText + headerSize, convertedSize);
  731.         delete converter; converter = nil;
  732.         fromH = DisposeIfHandle(fromH);
  733.         fi.Success();
  734.     }
  735.     else // fail
  736.     {
  737.         delete converter; converter = nil;
  738.         fromH = DisposeIfHandle(fromH);
  739.         fi.ReSignal();
  740.     }
  741. }
  742.  
  743. void TPostArticleCommand::PostArticle()
  744. {
  745.     FailInfo fi;
  746.     if (fi.Try())
  747.     {
  748.         fPostArticleInfo->CreateNntp();
  749.         gCurProgress->SetText(kPostArticleSendingText);
  750.         fPostArticleInfo->fNntp->PostArticle(fText, kPostArticleACKWait);
  751.         fi.Success();
  752.     }
  753.     else // fail
  754.     {
  755.         fPostArticleInfo->DiscardNntp();
  756.         fi.ReSignal();
  757.     }
  758. }
  759.  
  760. void TPostArticleCommand::TrashDiskCopy()
  761. {
  762.     FailInfo fi;
  763.     if (fi.Try())
  764.     {
  765.         FSSpec trashSpec, fromSpec, tmpSpec;
  766.         fPostArticleInfo->fFile->GetFileSpec(fromSpec);
  767.         tmpSpec = fromSpec;
  768.         trashSpec = fromSpec;
  769.         FailOSErr(FindFolder(trashSpec.vRefNum, kTrashFolderType, kCreateFolder, 
  770.             trashSpec.vRefNum, trashSpec.parID));
  771.         MakeFilenameUnique(trashSpec);
  772.         CStr255 tmpName;
  773.         while (true)
  774.         {
  775.             gCurThread->YieldTime();
  776.             NumToString(TickCount() & 0x7FFFFFFF, tmpName);
  777.             tmpName.Insert("Nuntius", 1);
  778.             CopyCString2String(tmpName, tmpSpec.name);
  779.             tmpSpec.vRefNum = fromSpec.vRefNum;
  780.             tmpSpec.parID = fromSpec.parID;
  781.             if (FileExist(tmpSpec))
  782.                 continue;
  783.             tmpSpec.vRefNum = trashSpec.vRefNum;
  784.             tmpSpec.parID = trashSpec.parID;
  785.             if (FileExist(trashSpec))
  786.                 continue;
  787.             break; // forgot this one in first place...
  788.         }
  789.         MyRenameFile(fPostArticleInfo->fFile, tmpName);
  790.         FailOSErr(fPostArticleInfo->fFile->MoveAndRename(trashSpec));
  791. #if 0
  792.         HParamBlockRec pb;
  793.         pb.copyParam.ioVRefNum = fromSpec.vRefNum;
  794.         pb.copyParam.ioDirID = fromSpec.parID;
  795.         pb.copyParam.ioNamePtr = fromSpec.name;
  796.         pb.copyParam.ioNewDirID = trashSpec.parID;
  797.         pb.copyParam.ioNewName = trashSpec.name;
  798.         pb.copyParam.ioCopyName = nil;
  799.         FailOSErr(PBHMoveRenameSync(&pb));
  800. #endif
  801.         fi.Success();
  802.     }
  803.     else // fail
  804.     {
  805.         if (fi.error)
  806.             gApplication->ShowError(fi.error, messageTrashPostedDocument);
  807.     }
  808. }
  809. //----------------------------------------------------------------------
  810. TCancelArticleCommand::TCancelArticleCommand()
  811. {
  812. }
  813.  
  814. pascal void TCancelArticleCommand::Initialize()
  815. {
  816.     inherited::Initialize();
  817. }
  818.  
  819. void TCancelArticleCommand::ICancelArticleCommand(const CStr255 &messageID)
  820. {
  821.     PPostArticleInfo *ai = new PPostArticleInfo();
  822.     ai->IPostArticleInfo();
  823.     inherited::IPostArticleCommand(ai);
  824.     ai->fReferences = messageID;
  825.     CStr255 s;
  826.     MyGetIndString(s, kCancelArticleProgressTitle);
  827.     ai->fSubject = s; // ugly ugly
  828. }
  829.     
  830. pascal void TCancelArticleCommand::Free()
  831. {
  832.     inherited::Free();
  833. }
  834.  
  835. pascal void TCancelArticleCommand::DoIt()
  836. {
  837.     FailInfo fi;
  838.     if (fi.Try())
  839.     {
  840.         inherited::DoIt();
  841.         fi.Success();
  842.     }
  843.     else // fail
  844.     {
  845.         if (fi.message == messageCouldNotPostArticle)
  846.             Failure(fi.error, messageCancelArticle);
  847.         else
  848.             fi.ReSignal();
  849.     }
  850. }
  851.  
  852. void TCancelArticleCommand::ReadArticle()
  853. {
  854.     THandleStream *handleStream = nil;
  855.     VOLATILE(handleStream);
  856.     FailInfo fi;
  857.     if (fi.Try())
  858.     {
  859.         fText = NewPermHandle(0);
  860.         THandleStream *hs = new THandleStream();
  861.         hs->IHandleStream(fText, 128);
  862.         handleStream = hs;
  863.         fPostArticleInfo->MakeHeader(handleStream, kCancelArticleHeaderStrings);
  864.         handleStream->Free(); handleStream = nil;
  865.         fi.Success();
  866.     }
  867.     else // fail
  868.     {
  869.         FreeIfObject(handleStream); handleStream = nil;
  870.         fi.ReSignal();
  871.     }
  872. }
  873.  
  874. void TCancelArticleCommand::PostArticle()
  875. {
  876.     FailInfo fi;
  877.     if (fi.Try())
  878.     {
  879.         fPostArticleInfo->CreateNntp();
  880.         gCurProgress->SetText(kCancelArticleSendingRequest);
  881.         fPostArticleInfo->fNntp->PostArticle(fText, kPostArticleACKWait);
  882.         fi.Success();
  883.     }
  884.     else // fail
  885.     {
  886.         fPostArticleInfo->DiscardNntp();
  887.         if (fi.error == noErr) // cancel
  888.             StdAlert(phPossiblePostedAnyway);
  889.         fi.ReSignal();
  890.     }
  891. }
  892.